﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using MoonLight.LibCommon;
using MoonLight.LibSocket;
using MoonLight.LibSession;
using Autumn.Common;
using Autumn.Business;

namespace Autumn.NetSession
{
    class AuSession : Session
    {
        public AuSession(Notifier parent)
        {
            m_sock = new AsynTcpSocket(this);
            m_parent = parent;
            m_parser = new CmdHandler();
            m_formator = new ReponseFomator();
            m_state = ProjectDefine.CmdState.STATE_UNKNOWN;
        }

        public void SetSockHandler(Socket handler)
        {
            if (m_sock.SetTcpSocket(handler) < 0) {
                Logger.WriteLog("set sock handler fail");
                Close();
            }
        }

        public override void Start()
        {
            Logger.WriteLog("session start...");
            if (m_sock.Receive() < 0) {
                Logger.WriteLog("sock receive fail");
                Close();
            }
        }

        public override void Close()
        {
            MessageInfo msg_info = new MessageInfo(ProjectDefine.ProjectMsgType_e.MSG_DEL_SESSION);
            msg_info.msg_data = this;
            m_parent.handle_message(msg_info);
        }

        public override void handle_message(MessageInfo msg)
        {
            switch ((CommonDefine.MsgType_e)msg.msg_type)
            {
                case CommonDefine.MsgType_e.MSG_TCP_READ:
                    {
                        string data = msg.msg_data.ToString();
                        if (m_state == ProjectDefine.CmdState.STATE_UPLOAD_START)
                        {
                            handle_upload_download(Encoding.UTF8.GetBytes(data));
                        }
                        else
                        {
                            Logger.WriteLog("data received from client, start parsing...");
                            if (m_parser.AddReq(data) < 0)
                            {
                                Logger.WriteLog("parse request fail, waiting for next request");
                            }
                            else
                                handle_request();
                        }
                        m_sock.Receive();
                    }
                    break;
                case CommonDefine.MsgType_e.MSG_TCP_SEND:
                    {
                        if (m_state == ProjectDefine.CmdState.STATE_DOWNLOAD_START)
                        {
                            handle_upload_download(null);
                        }
                        else
                            Logger.WriteLog("reponse sent");
                    }
                    break;
                case CommonDefine.MsgType_e.MSG_TCP_CLOSE:
                    {
                        Logger.WriteLog("connection closed");
                        Close();
                    }
                    break;
                case CommonDefine.MsgType_e.MSG_TCP_ERR:
                    {
                        SocketError err_code = (SocketError)msg.msg_data;
                        switch (err_code)
                        {
                            case SocketError.ConnectionReset:
                                {
                                    Logger.WriteLog("connection reset by peer, close session...");
                                    Close();
                                }
                                break;
                            default:
                                {
                                    Logger.WriteLog("connection error, close session...");
                                    Close();
                                }
                                break;
                        }
                    }
                    break;
                default:
                    {
                        Logger.WriteLog("unknow msg type [" + msg.msg_type + "]");
                    }
                    break;
            }
        }

        private void handle_upload_download(byte[] data)
        {
            if (m_state == ProjectDefine.CmdState.STATE_UPLOAD_START) 
            {
                try
                {
                    Logger.WriteLog("start uploading from client...");
                    File.WriteAllBytes(m_upload_file_path, data);
                    string md5 = CalMD5.GetMD5(m_upload_file_path);
                    if (md5 == m_upload_file_md5)
                    {
                        send_back(ProjectDefine.CmdType_e.CMD_UPLOAD, "D", "");
                    }
                    else {
                        Logger.WriteLog("handle CMD_UPLOAD fail [" + md5 + "]");
                        send_back(ProjectDefine.CmdType_e.CMD_UPLOAD, "F", md5);
                    }
                }
                catch (Exception ex) {
                    Logger.WriteLog("handle CMD_UPLOAD fail " + ex.Message);
                    send_back(ProjectDefine.CmdType_e.CMD_UPLOAD, "F", ex.Message);
                }
                m_state = ProjectDefine.CmdState.STATE_UPLOAD_END;
            }
            else if (m_state == ProjectDefine.CmdState.STATE_DOWNLOAD_START) 
            {
                Logger.WriteLog("finish downloading to client...");
                try
                {
                    send_back(ProjectDefine.CmdType_e.CMD_DOWNLOAD, "D", m_download_file_md5);
                }
                catch (Exception ex) {
                    Logger.WriteLog("handle CMD_DOWNLOAD fail " + ex.Message);
                    send_back(ProjectDefine.CmdType_e.CMD_DOWNLOAD, "F", ex.Message);
                }
                m_state = ProjectDefine.CmdState.STATE_DOWNLOAD_END;
            }
        }

        private void handle_request()
        { 
            ProjectDefine.CmdType_e type = m_parser.GetCmdType();
            switch (type)
            {
                case ProjectDefine.CmdType_e.CMD_UPLOAD:
                    {
                        Logger.WriteLog("handle CMD_UPLOAD");
                        try
                        {
                            m_upload_file_path = m_parser.GetCmdPathDST();
                            m_upload_file_md5 = m_parser.GetCmdContent();
                            m_state = ProjectDefine.CmdState.STATE_UPLOAD_START;   
                        }
                        catch (Exception ex)
                        {
                            Logger.WriteLog("handle CMD_UPLOAD fail " + ex.Message);
                            send_back(ProjectDefine.CmdType_e.CMD_UPLOAD, "F", ex.Message);
                            m_state = ProjectDefine.CmdState.STATE_UPLOAD_END;
                        }
                    }
                    break;
                case ProjectDefine.CmdType_e.CMD_DOWNLOAD:
                    {
                        Logger.WriteLog("handle CMD_DOWNLOAD");
                        try
                        {
                            m_download_file_path = m_parser.GetCmdPathSRC();
                            m_download_file_md5 = CalMD5.GetMD5(m_parser.GetCmdPathSRC());
                            byte[] bytes = File.ReadAllBytes(m_parser.GetCmdPathSRC());
                            m_sock.Send(bytes);
                            m_state = ProjectDefine.CmdState.STATE_DOWNLOAD_START;
                        }
                        catch (Exception ex)
                        {
                            Logger.WriteLog("handle CMD_DOWNLOAD fail " + ex.Message);
                            send_back(ProjectDefine.CmdType_e.CMD_DOWNLOAD, "F", ex.Message);
                            m_state = ProjectDefine.CmdState.STATE_DOWNLOAD_END;
                        }
                    }
                    break;
                case ProjectDefine.CmdType_e.CMD_EXECUTE:
                    {
                        Logger.WriteLog("handle CMD_EXECUTE");
                        string result = "";
                        if (m_parser.GetCmdPara() == "")
                        {
                            result = CmdPipe.StartCmd(m_parser.GetCmdCmd());
                        }
                        else
                        {
                            result = CmdPipe.StartCmdArgs(m_parser.GetCmdCmd(), m_parser.GetCmdPara());
                        }
                        send_back(ProjectDefine.CmdType_e.EXECUTE_REP, result, "");
                    }
                    break;
                default:
                    {
                        Logger.WriteLog("unknow request type [" + type + "]");
                        send_back(ProjectDefine.CmdType_e.UNKNOWN_REP, "UNKNOWN", "");
                    }
                    break;
            }
        }

        private void send_back(ProjectDefine.CmdType_e type, string result, string content)
        {
            Logger.WriteLog("send reponse to client...");
            string reponse = "";
            switch (type)
            {
                case ProjectDefine.CmdType_e.CMD_UPLOAD:
                    {
                        Logger.WriteLog("reponse CMD_UPLOAD");
                        reponse = m_formator.GetResponse("UPLOAD_REP", result, content);
                    }
                    break;
                case ProjectDefine.CmdType_e.CMD_DOWNLOAD:
                    {
                        Logger.WriteLog("reponse CMD_DOWNLOAD");
                        reponse = m_formator.GetResponse("DOWNLOAD_REP", result, content);
                    }
                    break;
                case ProjectDefine.CmdType_e.EXECUTE_REP:
                    {
                        Logger.WriteLog("reponse CMD_EXECUTE");
                        reponse = m_formator.GetResponse("EXECUTE_REP", result, "");
                    }
                    break;
                default:
                    {
                        Logger.WriteLog("reponse unknow request type [" + type + "]");
                        reponse = m_formator.GetResponse("UNKNOWN", "UNKNOWN", "");
                    }
                    break;
            }
            byte[] bytes = Encoding.UTF8.GetBytes(reponse);
            m_sock.Send(bytes);
        }

        private AsynTcpSocket m_sock;
        private Notifier m_parent;
        private CmdHandler m_parser;
        private ReponseFomator m_formator;
        private string m_upload_file_path;
        private string m_upload_file_md5;
        private string m_download_file_path;
        private string m_download_file_md5;
        private ProjectDefine.CmdState m_state;
    }
}
